<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>算术题生成器</title>
    <style>
        #count{
            margin-top: 1rem;
        }
    </style>
</head>
<body>
    <div class="layui-card">
        <div class="layui-card-header">
            算术题生成器
        </div>
        <div class="layui-card-body">
            <div class="layui layui-form">
                <div class="layui-form-item">
                    <label class="layui-form-label">说明</label>
                    <div class="layui-input-block">
                        <div class="layui-form-mid">
                            不同规则用“|”分开<br/>
                            数字范围有两种形式：1~5或者2,3,4<br/>
                            运算符号：+-*/不用加逗号分隔，会自动判定<br/>
                            举例：[1~3][+-][2,4][*][3,4]|
                        </div>
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">规则</label>
                    <div class="layui-input-block">
                        <textarea id="rule" class="layui-textarea">[1~3][+-][2,4][*][3,4]</textarea>
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">反向选择</label>
                    <div class="layui-input-block">
                        <input class="layui-input" type="radio" name="reverse" title="否" value="0" checked>
                        <input class="layui-input" type="radio" name="reverse" title="是" value="1" >
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">只有正数</label>
                    <div class="layui-input-block">
                        <input class="layui-input" type="radio" name="positive" title="否" value="0" checked>
                        <input class="layui-input" type="radio" name="positive" title="是" value="1" >
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">标题字号</label>
                    <div class="layui-input-block">
                        <input type="number" value="45" id="question-size" class="layui-input">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">选项字号</label>
                    <div class="layui-input-block">
                        <input type="number" id="answer-size" value="50" class="layui-input">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">数字颜色</label>
                    <div class="layui-input-block">
                        <input class="layui-input" type="radio" name="option-black" title="白色" value="1" checked>
                        <input class="layui-input" type="radio" name="option-black" title="彩色" value="0" >
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">颜色种类</label>
                    <div class="layui-input-block">
                        <input type="number" id="color-count" value="4" class="layui-input">

                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">题目数量</label>
                    <div class="layui-input-block">
                        <input type="number" id="count" value="10" class="layui-input">
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">生成配置</label>
                    <div class="layui-input-block">
                        <button class="layui-btn" onclick="generateString()">
                            生成
                        </button>
                    </div>
                </div>
                <div class="layui-form-item">
                    <label class="layui-form-label">生成结果</label>
                    <div class="layui-input-block">
                        <textarea id="result" rows="12" class="layui-textarea"></textarea>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
<script>
function generateString(){
    let isPositive = $('input[name="positive"]:checked').val() == 1
    let count = $('#count').val() ? $('#count').val() : 10;
    const rules = $('#rule').val().split('|')
    let generateRule = []
    for(let i = 0; i < rules.length; i++) {
        if(!rules[i]) {
            continue
        }
        let rule = getRule(rules[i])
        generateRule[i] = []
        for(let j = 0; j < rule.length; j++) {
            // 如果是空的就不管
            if(!rule[j]) {
                continue
            }
            if(isDigit(rule[j][0])) {
                // 包含~
                if(rule[j].indexOf('~') > -1) {
                    let range = rule[j].split('~')
                    generateRule[i].push(generateRange(range[0], range[1]))
                }
                else{
                    generateRule[i].push(rule[j].split(','))
                }
            } 
            else {
                generateRule[i].push(rule[j].split(''))
            }
        }
    }
    let questionList = []
    for(let i = 0; i < count; i ++){
        let questionItem = generateFormula(generateRule);
        if(isPositive){
            if(questionItem.right < 0){
                i --
                continue
            }
        }
        questionList.push(questionItem)
    }
    console.log(questionList);
    let result = generateResult(questionList);
    $('#result').val(result);
    // 然后开始根据这个数组生成公式和答案
    // let result = JSON.stringify(noteList);
    // $('#result').val(result);
}
function getRandomColor(){
    let limit = $('#color-count').val();
    const colorHex = {
        red: '#FF0000',
        green: '#00B600',
        blue: '#0000FF',
        yellow: '#FFFF00',
        black: '#000000',
        white: '#FFFFFF',
        purple: '#800080',
        orange: '#FF6500',
        // gray: '#808080',
        brown: '#8B4513',
        pink: '#FFC0CB',
        cyan: '#00FFFF'
    }
    // 便利对象
    const colorArr = [];
    for(let key in colorHex){
        colorArr.push(key);
    }
    if(!limit || limit > colorArr.length){
        limit = colorArr.length;
    }
    let randomColor = colorArr[Math.floor(Math.random() * limit)];
    return colorHex[randomColor];
}
function generateResult(res){
    let questionSize = $('#question-size').val();
    let optionSize = $('#answer-size').val();
    let isBlack = $('input[name="option-black"]:checked').val() == 1
    let isReverse = $('input[name="reverse"]:checked').val() == 1
    let qtag1 = '<size=' + questionSize + '>' + (isBlack ? '<outline color=#000000 width=1>' : '');
    let qtag2 = (isBlack ? '</outline>' : '') + '</size>';
    let otag1 = (optionSize ? '<size=' + optionSize + '>' : '') + (isBlack ? '<outline color=#000000 width=1>' : '')
    let otag2 = (isBlack ? '</outline>' : '') + (optionSize ? '</size>' : '');
    let configArr = [];
    for(let i = 0;i<res.length;i++){
        let item = res[i];
        let questionTag = item.question;
        let rightTag = item.right;
        if(!isBlack){
            questionTag = getColorTag(questionTag, true)
            rightTag = getColorTag(rightTag);
        }
        let configItem = {
            question: qtag1 + "<b>" + questionTag + "</b>" + qtag2,
            rightList: [otag1 + "<b>" + rightTag + "</b>" + otag2],
            wrongList: []
        }
        for(let j = 0;j<item.wrongList.length;j++){
            let wrongItem = item.wrongList[j];
            if(!isBlack){
                wrongItem = getColorTag(wrongItem);
            }
            configItem.wrongList.push( otag1 + "<b>" + wrongItem + "</b>" + otag2); 
        }
        if(isReverse){
            let tmp = configItem.rightList
            configItem.rightList = configItem.wrongList
            configItem.wrongList = tmp
        }
        configArr.push(configItem);
    }
    return JSON.stringify(configArr)
}
function getColorTag(str, isSeperate = false){
    if(isSeperate){
        return str.split('').map(char => '<color=' + getRandomColor() + '>' + char + '</color>').join('')
    }
    else{
        return '<color=' + getRandomColor() + '>' + str + '</color>'
    }
}
function generateFormula(generateRule) {
    let formula = ''
    let rule = generateRule[Math.floor(Math.random() * generateRule.length)]
    for(let i = 0; i < rule.length; i++) {
        formula += rule[i][Math.floor(Math.random() * rule[i].length)]
    }
    // 这里看一下输出的样子来调整×÷
    let formulaStr = formula.replaceAll('*','\u00d7').replaceAll('/','\u00f7').replaceAll('+','\u002b').replaceAll('-','\u2212')
    let answer = calculate(formula)
    // 这里做一些简单的规则干扰项设定×
    // 简单的+1 -1， 
    let wrongCalc = [1, -1]
    if(answer > 10 || answer < -10){
        wrongCalc.push(10, -10)
    }
    let wrongList = wrongCalc.map(item => item + answer)
    // 如果干扰计算得到的结果和真实答案不一致
    if(calculate(formula, false) != answer){
        wrongList.push(calculate(formula, false))
    }
    return {
        question: formulaStr,
        right: answer,
        wrongList: wrongList
    }
}
function getRule(str) {
    const regex = /\[([^\]]*)\]/g;
    const result = [];
    let match;
    while ((match = regex.exec(str)) !== null) {
        result.push(match[1]);
    }
    return result;
}
function generateRange(x, y) {
    if(x == y){
        return [x]
    }
    x = Number(x)
    y = Number(y)
    if(x > y){
        let tmp = x;
        x = y;
        y = tmp;
    }
    console.log(x, y);
    return Array.from({ length: y - x + 1 }, (_, i) => (x + i).toString());
}
function isDigit(char) {
  return /^\d$/.test(char);
}
function calculate(formula, isRight = true) {

    // 去除空格
    formula = formula.replace(/\s/g, '');
    
    // 检查括号是否匹配
    if (!checkParentheses(formula)) {
        return "括号不匹配";
    }
    
    // 中缀表达式转后缀表达式
    const postfix = infixToPostfix(formula, isRight);

    
    // 计算后缀表达式
    return evaluatePostfix(postfix);
}

function checkParentheses(formula) {
    let stack = [];
    for (let char of formula) {
        if (char === '(') {
            stack.push(char);
        } else if (char === ')') {
            if (stack.length === 0 || stack.pop() !== '(') {
                return false;
            }
        }
    }
    return stack.length === 0;
}

function infixToPostfix(formula, isRight) {
    const precedence = {
        '+': isRight ? 1 : 2,  
        '-': isRight ? 1 : 2,   
        '*': isRight ? 2 : 1,  
        '/': isRight ? 2 : 1,  
    };
    let stack = [];
    let postfix = [];
    
    for (let i = 0; i < formula.length; i++) {
        const char = formula[i];
        
        // 处理数字
        if (/^\d$/.test(char)) {
            let num = '';
            while (i < formula.length && /^\d$/.test(formula[i])) {
                num += formula[i];
                i++;
            }
            i--; // 回退一个位置，因为for循环会再次递增i
            postfix.push(parseFloat(num));
            continue;
        }
        
        // 处理左括号
        if (char === '(') {
            stack.push(char);
        }
        // 处理右括号
        else if (char === ')') {
            while (stack.length > 0 && stack[stack.length - 1] !== '(') {
                postfix.push(stack.pop());
            }
            stack.pop(); // 弹出左括号
        }
        // 处理运算符
        else {
            while (
                stack.length > 0 && 
                stack[stack.length - 1] !== '(' && 
                (precedence[stack[stack.length - 1]] >= precedence[char])
            ) {
                postfix.push(stack.pop());
            }
            stack.push(char);
        }
    }
    
    // 弹出剩余的运算符
    while (stack.length > 0) {
        postfix.push(stack.pop());
    }
    return postfix;
}

function evaluatePostfix(postfix) {
    let stack = [];
    
    for (let token of postfix) {
        if (typeof token === 'number') {
            stack.push(token);
        } else {
            const b = stack.pop();
            const a = stack.pop();
            switch (token) {
                case '+':
                    stack.push(a + b);
                    break;
                case '-':
                    stack.push(a - b);
                    break;
                case '*':
                    stack.push(a * b);
                    break;
                case '/':
                    stack.push(a / b);
                    break;
            }
        }

    }
    
    return stack[0];
}
</script>
</html>